﻿using Brc_DSB.GetWay.Polly;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration;
using Ocelot.Logging;
using Ocelot.Requester;
using Ocelot.Requester.QoS;
using Ocelot.Responses;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using Brc_DSB.GetWay.Extention;
using System.Linq;
using Brc_DSB.GetWay.Configuration;

namespace Brc_DSB.GetWay.Requester
{
    public class ExtDelegatingHandlerHandlerFactory : IDelegatingHandlerHandlerFactory
    {
        private readonly ITracingHandlerFactory _tracingFactory;
        private readonly IQoSFactory _qoSFactory;
        private readonly IRetryFactory _retryFactory;
        private readonly IServiceProvider _serviceProvider;
        private readonly IOcelotLogger _logger;
        private readonly DbConfiguration _option;

        public ExtDelegatingHandlerHandlerFactory(
            ITracingHandlerFactory tracingFactory,
            IQoSFactory qoSFactory,
            IRetryFactory retryFactory,
            IServiceProvider serviceProvider,
            IOcelotLoggerFactory loggerFactory, DbConfiguration option)
        {
            _logger = loggerFactory.CreateLogger<DelegatingHandlerHandlerFactory>();
            _serviceProvider = serviceProvider;
            _tracingFactory = tracingFactory;
            _qoSFactory = qoSFactory;
            _retryFactory = retryFactory;
            _option = option;
        }

        public Response<List<Func<DelegatingHandler>>> Get(DownstreamReRoute downstreamRoute)
        {
            var globalDelegatingHandlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();

            var routeSpecificHandlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();

            var handlers = new List<Func<DelegatingHandler>>();

            foreach (var handler in globalDelegatingHandlers)
            {
                if (GlobalIsInHandlersConfig(downstreamRoute, handler))
                {
                    routeSpecificHandlers.Add(handler.DelegatingHandler);
                }
                else
                {
                    handlers.Add(() => handler.DelegatingHandler);
                }
            }

            if (downstreamRoute.DelegatingHandlers.Any())
            {
                var sorted = SortByConfigOrder(downstreamRoute, routeSpecificHandlers);

                foreach (var handler in sorted)
                {
                    handlers.Add(() => handler);
                }
            }

            if (downstreamRoute.HttpHandlerOptions.UseTracing)
            {
                handlers.Add(() => (DelegatingHandler)_tracingFactory.Get());
            }

            if (downstreamRoute.QosOptions.UseQos)
            {
                var handler = _qoSFactory.Get(downstreamRoute);

                if (handler != null && !handler.IsError)
                {
                    handlers.Add(() => handler.Data);
                }
                else
                {
                    _logger.LogWarning($"Route {downstreamRoute.UpstreamPathTemplate} specifies use QoS but no QosHandler found in DI container. Will use not use a QosHandler, please check your setup!");
                    handlers.Add(() => new NoQosDelegatingHandler());
                }
            }
            if(_option.IsEnableRetry)
            {
                var handler = _retryFactory.Get(downstreamRoute);
                if (handler != null && !handler.IsError)
                {
                    handlers.Add(() => handler.Data);
                }
                else
                {
                    _logger.LogWarning($"Route {downstreamRoute.UpstreamPathTemplate} specifies use Retry but no RetryHandler found in DI container. Will use not use a RetryHandler, please check your setup!");
                    handlers.Add(() => new NoQosDelegatingHandler());
                }
            }

            return new OkResponse<List<Func<DelegatingHandler>>>(handlers);
        }

        private List<DelegatingHandler> SortByConfigOrder(DownstreamReRoute request, List<DelegatingHandler> routeSpecificHandlers)
        {
            return routeSpecificHandlers
                .Where(x => request.DelegatingHandlers.Contains(x.GetType().Name))
                .OrderBy(d =>
                {
                    var type = d.GetType().Name;
                    var pos = request.DelegatingHandlers.IndexOf(type);
                    return pos;
                }).ToList();
        }

        private bool GlobalIsInHandlersConfig(DownstreamReRoute request, GlobalDelegatingHandler handler)
        {
            return request.DelegatingHandlers.Contains(handler.DelegatingHandler.GetType().Name);
        }
    }
}
